package org.wwx.protocol;

import org.apache.commons.io.IOUtils;
import org.wwx.common.Invocation;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 *  HTTP 客户端，支持多种请求方法和内容类型
 *
 * @date 2025/6/5
 * @author王闻薪
 */
public class HttpClient {

    /**
     * 发送 RPC 调用请求（原始方法）
     *
     * @param hostname 主机名
     * @param port 端口号
     * @param invocation 调用对象
     * @return 响应内容
     */
    public String send(String hostname, Integer port, Invocation invocation) {
        try {
            URL url = new URL("http", hostname, port, "/");
            return sendPost(url, invocation, "application/java-serialized-object");
        } catch (IOException e) {
            throw new RuntimeException("RPC调用失败: " + e.getMessage(), e);
        }
    }

    /**
     * 发送 GET 请求
     *
     * @param urlString 目标URL
     * @return 响应内容
     */
    public String get(String urlString) {
        return get(urlString, null);
    }

    /**
     * 发送带请求头的 GET 请求
     *
     * @param urlString 目标URL
     * @param headers 请求头映射
     * @return 响应内容
     */
    public String get(String urlString, Map<String, String> headers) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");

            // 设置请求头
            setHeaders(conn, headers);

            return handleResponse(conn);
        } catch (IOException e) {
            throw new RuntimeException("GET请求失败: " + urlString, e);
        }
    }

    /**
     * 发送 POST 请求（表单格式）
     *
     * @param urlString 目标URL
     * @param formData 表单数据映射
     * @return 响应内容
     */
    public String postForm(String urlString, Map<String, String> formData) {
        return post(urlString,
                encodeFormData(formData),
                "application/x-www-form-urlencoded",
                null);
    }

    /**
     * 发送 POST 请求（JSON 格式）
     *
     * @param urlString 目标URL
     * @param jsonData JSON 数据
     * @return 响应内容
     */
    public String postJson(String urlString, String jsonData) {
        return post(urlString, jsonData, "application/json", null);
    }

    /**
     * 发送通用 POST 请求
     *
     * @param urlString 目标URL
     * @param data 请求体数据
     * @param contentType 内容类型
     * @param headers 额外请求头
     * @return 响应内容
     */
    public String post(String urlString, String data, String contentType, Map<String, String> headers) {
        try {
            URL url = new URL(urlString);
            return sendPost(url, data, contentType, headers);
        } catch (IOException e) {
            throw new RuntimeException("POST请求失败: " + urlString, e);
        }
    }

    /**
     * 发送 PUT 请求
     *
     * @param urlString 目标URL
     * @param data 请求体数据
     * @param contentType 内容类型
     * @return 响应内容
     */
    public String put(String urlString, String data, String contentType) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("PUT");
            conn.setDoOutput(true);
            conn.setRequestProperty("Content-Type", contentType);

            try (OutputStream os = conn.getOutputStream()) {
                os.write(data.getBytes(StandardCharsets.UTF_8));
            }

            return handleResponse(conn);
        } catch (IOException e) {
            throw new RuntimeException("PUT请求失败: " + urlString, e);
        }
    }

    /**
     * 发送 DELETE 请求
     *
     * @param urlString 目标URL
     * @return 响应状态码
     */
    public int delete(String urlString) {
        try {
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("DELETE");
            return conn.getResponseCode();
        } catch (IOException e) {
            throw new RuntimeException("DELETE请求失败: " + urlString, e);
        }
    }

    // =============== 内部实现方法 ===============

    private String sendPost(URL url, Object data, String contentType) throws IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", contentType);

        try (ObjectOutputStream oos = new ObjectOutputStream(conn.getOutputStream())) {
            oos.writeObject(data);
        }

        return handleResponse(conn);
    }

    private String sendPost(URL url, String data, String contentType, Map<String, String> headers)
            throws IOException {

        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);
        conn.setRequestProperty("Content-Type", contentType);

        // 设置额外请求头
        setHeaders(conn, headers);

        try (OutputStream os = conn.getOutputStream()) {
            os.write(data.getBytes(StandardCharsets.UTF_8));
        }

        return handleResponse(conn);
    }

    private String handleResponse(HttpURLConnection conn) throws IOException {
        int status = conn.getResponseCode();
        if (status >= 200 && status < 300) {
            try (InputStream is = conn.getInputStream()) {
                return IOUtils.toString(is, StandardCharsets.UTF_8);
            }
        } else {
            try (InputStream es = conn.getErrorStream()) {
                String errorBody = es != null ?
                        IOUtils.toString(es, StandardCharsets.UTF_8) : "No error body";
                throw new IOException("HTTP错误: " + status + " - " + errorBody);
            }
        }
    }

    private void setHeaders(URLConnection conn, Map<String, String> headers) {
        if (headers != null) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                conn.setRequestProperty(entry.getKey(), entry.getValue());
            }
        }
    }

    private String encodeFormData(Map<String, String> formData) {
        if (formData == null || formData.isEmpty()) {
            return "";
        }

        StringBuilder result = new StringBuilder();
        for (Map.Entry<String, String> entry : formData.entrySet()) {
            if (result.length() > 0) {
                result.append("&");
            }
            result.append(URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8));
            result.append("=");
            result.append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8));
        }
        return result.toString();
    }

    /**
     * 设置全局连接超时和读取超时（毫秒）
     *
     * @param connectTimeout 连接超时时间
     * @param readTimeout 读取超时时间
     */
    public void setTimeouts(int connectTimeout, int readTimeout) {
        System.setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(connectTimeout));
        System.setProperty("sun.net.client.defaultReadTimeout", String.valueOf(readTimeout));
    }
}